package com.hyt.it.ogt.kq.service.gov.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.map.MapBuilder;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.template.Template;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateEngine;
import cn.hutool.extra.template.engine.thymeleaf.ThymeleafEngine;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.google.common.collect.Lists;
import com.hyt.common.apiInfor.ApiResponse;
import com.hyt.core.base.BaseEntity;
import com.hyt.exception.exception.BusinessRequestException;
import com.hyt.exception.exception.InvalidRequestException;
import com.hyt.html2file.enums.HtmlToXEnum;

import com.hyt.it.ogt.kq.common.config.ConfigManager;
import com.hyt.it.ogt.kq.common.gov.constant.ResponseCode;
import com.hyt.it.ogt.kq.service.bm.den.UploadFileToHuaweiUtil;
import com.hyt.it.ogt.kq.service.gov.mapper.TaskSignMapper;
import com.hyt.it.ogt.kq.service.gov.model.dto.ProgressResult;
import com.hyt.it.ogt.kq.service.gov.model.entity.*;
import com.hyt.it.ogt.kq.service.gov.model.param.DeskTopExportParam;
import com.hyt.it.ogt.kq.service.gov.model.vo.CandidateVo;
import com.hyt.it.ogt.kq.service.gov.model.vo.SignVo;
import com.hyt.it.ogt.kq.service.gov.model.vo.TaskRoomVo;
import com.hyt.it.ogt.kq.service.gov.model.vo.arrange.ArrangeResultVO;
import com.hyt.it.ogt.kq.service.gov.service.ITaskService;
import com.hyt.it.ogt.kq.service.gov.service.ITaskSignService;
import com.hyt.it.ogt.kq.service.gov.service.ITimeRoomCandidateService;
import com.hyt.it.ogt.kq.service.gov.service.ITimeService;
import com.hyt.it.ogt.kq.service.gov.service.obs.CloudObsService;
import com.hyt.progress.service.ProgressManager;
import com.hyt.progress.threadpool.AsyncListenableProgressThreadPool;
import com.hyt.progress.updater.anno.ProgressUpdate;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
@Slf4j
public class TaskSignImpl implements ITaskSignService {
    @Resource
    ConfigManager globalConfig;
    @Resource
    TaskSignMapper taskSignMapper;
    @Resource
    ITaskService iTaskService;
    @Resource
    private CloudObsService cloudObsService;
    String HTML_SUFFIX = ".html";
    @Resource
    ITimeService iTimeService;
    @Resource
    ITimeRoomCandidateService iTimeRoomCandidateService;




    public void exportDeskTop(DeskTopExportParam param){
        Task task = iTaskService.getTaskById(param.getTaskId());
        param.setTaskName(task.getName());
        List<CandidateVo> list = taskSignMapper.queryCandidate(param.getTaskId());
        List<TaskRoomVo> roomList = taskSignMapper.queryTimeRoom(param.getTaskId());
        if(CollectionUtil.isEmpty(list) || CollectionUtil.isEmpty(roomList)){
            BusinessRequestException.throwException(ResponseCode.EXPORT_DESKTOP_NO_ARRANGE_ERROR.getCode(), "没有编排数据，无法导出桌贴");
        }
        ProgressManager.updateProgressCurrent(10L, "查询编排数据成功");
        roomList.stream().forEach(c ->{
            List<CandidateVo> voList = list.stream().filter(candidateVo -> c.getRoomId().equals(candidateVo.getRoomId()) && c.getTimeId().equals(candidateVo.getTimeId())).collect(Collectors.toList());
            c.setCandidateVoList(voList);
        });
        String path = generateDeskTop(roomList,param);
        if(StrUtil.isNotBlank(path)){
            ProgressManager.finish(ProgressResult.builder().success(Boolean.TRUE).result(path).build());
        }else{
            ProgressManager.finish(ProgressResult.builder().success(Boolean.FALSE).result("下载失败").build());
        }
    }

    @Override
    public SignVo querySign(String taskId) {
        SignVo vo = new SignVo();
        List<Time> timeList = iTimeService.lambdaQuery().eq(Time::getTaskId, taskId).eq(BaseEntity::getDelFlag, false)
                .orderByAsc(Time::getCode).list();
        int count = 0;
        for (Time time : timeList) {
            // 已编排考生数
            int candidateNum = iTimeRoomCandidateService.lambdaQuery().eq(TimeRoomCandidate::getTaskId, taskId)
                    .eq(TimeRoomCandidate::getTimeId, time.getId()).eq(BaseEntity::getDelFlag, false).count();
            count += candidateNum;
        }
        vo.setArrangeNum(count);
        vo.setPath(globalConfig.getSignPath());
        return vo;
    }

    public void exportSign(String taskId){
        Task task = iTaskService.getTaskById(taskId);
        List<CandidateVo> list = taskSignMapper.queryCandidate(taskId);
        List<TaskRoomVo> roomList = taskSignMapper.queryTimeRoom(taskId);
        if(CollectionUtil.isEmpty(list) || CollectionUtil.isEmpty(roomList)){
            BusinessRequestException.throwException(ResponseCode.EXPORT_SIGN_NO_ARRANGE_ERROR.getCode(), "没有编排数据，无法导出签到表");
        }
        ProgressManager.updateProgressCurrent(10L, "查询编排数据成功");
        roomList.stream().forEach(c ->{
            List<CandidateVo> voList = list.stream().filter(candidateVo -> c.getRoomId().equals(candidateVo.getRoomId()) && c.getTimeId().equals(candidateVo.getTimeId())).collect(Collectors.toList());
            c.setCandidateVoList(voList);
        });
        String path = generateSign(roomList,task);
        if(StrUtil.isNotBlank(path)){
            ProgressManager.finish(ProgressResult.builder().success(Boolean.TRUE).result(path).build());
        }else{
            ProgressManager.finish(ProgressResult.builder().success(Boolean.FALSE).result("下载失败").build());
        }
    }


    @SneakyThrows
    public String generateSign(List<TaskRoomVo> roomList,Task task){
        String fileUrl = "";
        String fileName = task.getName()+"_"+ System.currentTimeMillis();
        File tempFile = FileUtil.createTempFile(fileName,".zip", true);
        ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(tempFile));
        try{
            Map<String, File> timeIdMappingFile = roomList.stream()
                    .map(room -> {
                        List<CandidateVo> candidateVoList = room.getCandidateVoList();
                        // 生成准考证HTML文件
                        File admissionTicketFile = null;
                        try {
                            admissionTicketFile = generateSignHTML(room, candidateVoList,task);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        return MapBuilder.<String, File>create()
                                .put(room.getTimeId()+"_"+room.getRoomId(), admissionTicketFile)
                                .build();
                    }).map(Map::entrySet)
                    .flatMap(Collection::stream)
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
            ProgressManager.updateProgressCurrent(40L, "生成签到html成功");
            Map<String, File> timeIdMappingFuturePDFFile = roomList.stream()
                    .map(room -> {
                        File admissionTicketFile = timeIdMappingFile.get(room.getTimeId()+"_"+room.getRoomId());
                        // 上传准考证PDF文件
                    File execute = HtmlToXEnum.PDF.execute(admissionTicketFile.getPath());
                    log.debug("# 1-5、签到PDF文件生成完成");
                    InputStream inputStream = null;
                    try{
                        inputStream = FileUtil.getInputStream(execute);
                        ZipEntry entry = new ZipEntry(room.getTimeName()+"_"+room.getPlaceName()+"_"+room.getRoomName()+".pdf");
                        zipOutputStream.putNextEntry(entry);
                        IoUtil.copy(inputStream, zipOutputStream);
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        IoUtil.close(inputStream);
                        execute.delete();
                    }
                    return MapBuilder.<String, File>create()
                            .put(room.getTimeId()+"_"+room.getRoomId(), execute)
                            .build();
                }).map(Map::entrySet)
                .flatMap(Collection::stream)
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        	ProgressManager.updateProgressCurrent(40L, "生成桌贴pdf成功");

              
            zipOutputStream.flush();
            String path = "sign/"+task.getId()+"/"+tempFile.getName();
            fileUrl = cloudObsService.updateFile(path, tempFile);
            ProgressManager.updateProgressCurrent(80L, "上传桌贴zip包成功");


        } catch (Exception ex){
            ex.printStackTrace();
        } finally {
            IoUtil.close(zipOutputStream);
        }
        return fileUrl;
    }

    @SneakyThrows
    public String generateDeskTop(List<TaskRoomVo> roomList, DeskTopExportParam param) {
        String fileUrl = "";
        String fileName = param.getTaskName() + "_" + System.currentTimeMillis();
        File tempFile = FileUtil.createTempFile(fileName, ".zip", true);
        ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(tempFile));
        try{
        Map<String, File> timeIdMappingFile = roomList.stream()
                .map(room -> {
                    List<CandidateVo> candidateVoList = room.getCandidateVoList();
                    // 生成准考证HTML文件
                    File admissionTicketFile = null;
                    try {
                        admissionTicketFile = generateDeskTopHTML(room, candidateVoList, param);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return MapBuilder.<String, File>create()
                            .put(room.getTimeId()+"_"+room.getRoomId(), admissionTicketFile)
                            .build();
                }).map(Map::entrySet)
                .flatMap(Collection::stream)
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        ProgressManager.updateProgressCurrent(40L, "生成桌贴html成功");
        Map<String, File> timeIdMappingFuturePDFFile = roomList.stream()
                .map(room -> {
                    File admissionTicketFile = timeIdMappingFile.get(room.getTimeId()+"_"+room.getRoomId());
                    // 上传准考证PDF文件
                    File execute = HtmlToXEnum.PDF.execute(admissionTicketFile.getPath());
                    log.debug("# 1-5、桌贴PDF文件生成完成");
                    InputStream inputStream = null;
                    try{
                        inputStream = FileUtil.getInputStream(execute);
                        ZipEntry entry = new ZipEntry(room.getTimeName()+"_"+room.getPlaceName()+"_"+room.getRoomName()+".pdf");
                        zipOutputStream.putNextEntry(entry);
                        IoUtil.copy(inputStream, zipOutputStream);
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        IoUtil.close(inputStream);
                        execute.delete();
                    }
                    return MapBuilder.<String, File>create()
                            .put(room.getTimeId()+"_"+room.getRoomId(), execute)
                            .build();
                }).map(Map::entrySet)
                .flatMap(Collection::stream)
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
       	 ProgressManager.updateProgressCurrent(40L, "生成桌贴pdf成功");

            zipOutputStream.flush();

            String path = "sign/" + param.getTaskId() + "/" + tempFile.getName();
            fileUrl = cloudObsService.updateFile(path, tempFile);
            ProgressManager.updateProgressCurrent(80L, "上传桌贴zip包成功");
        } catch(Exception ex){
           ex.printStackTrace();
        } finally {
            IoUtil.close(zipOutputStream);
        }
        return fileUrl;
    }

    private Map<String, Object> generateMap(Map<String, Object> objectMap,TaskRoomVo taskRoom){
        objectMap.put("roomName",taskRoom.getRoomName());
        objectMap.put("placeName",taskRoom.getPlaceName());
        objectMap.put("timeName",taskRoom.getTimeName());
        String startTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
                .format(taskRoom.getStartTime());
        String endTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
                .format(taskRoom.getEndTime());
        objectMap.put("timeRange",startTime+"至"+ endTime);
        return objectMap;
    }

    public File generateDeskTopHTML(TaskRoomVo taskRoom, List<CandidateVo> candidateVos, DeskTopExportParam param) throws IOException {
        Map<String, Object> objectMap = new HashMap<>();
        objectMap = generateMap(objectMap,taskRoom);
        objectMap.put("taskName",param.getTaskName());
        List<Dict> dictList = param.getDictList();
        Map<String, String> map = dictList.stream().collect(Collectors.toMap(Dict::getValue,Dict::getLabel));
        //10个一组为一页
        int partition = "1".equals(param.getTemplatePath())? 30 : 10;
        List<List<CandidateVo>> parts = Lists.partition(candidateVos,partition);
        List<List<Map<String, String>>> resultList = new ArrayList<>();
        parts.stream().forEach(candidateList ->{
            List<Map<String, String>> list = new ArrayList<>();
            candidateList.stream().forEach(c -> {
                Map<String, String> resultMap = new HashMap<>();
                if(map.containsKey("name")){
                    resultMap.put("name",c.getCandidateName());
                    resultMap.put("nameLabel",map.get("name"));
                }
                if(map.containsKey("sex")){
                    resultMap.put("sex",c.getSex());
                    resultMap.put("sexLabel",map.get("sex"));
                }
                if(map.containsKey("idcard")){
                    resultMap.put("idcard",c.getCardNumber());
                    resultMap.put("idcardLabel",map.get("idcard"));
                }
                if(map.containsKey("admission")){
                    resultMap.put("admission",c.getAdmissionNumber());
                    resultMap.put("admissionLabel",map.get("admission"));
                }
                if(map.containsKey("subject")){
                    resultMap.put("subject",c.getSubjectName());
                    resultMap.put("subjectLabel",map.get("subject"));
                }
                if(map.containsKey("photo")){
                    if(StrUtil.isBlank(c.getPhoto())){
                      resultMap.put("photo",null);
                    }else{
                      resultMap.put("photo",c.getPhoto());
                    }
                    resultMap.put("photoLabel",map.get("photo"));
                }
                if(map.containsKey("seat")){
                    resultMap.put("seat",c.getSeat());
                    resultMap.put("seatLabel",map.get("seat"));
                }
                if(map.containsKey("sign")){
                    resultMap.put("sign","");
                    resultMap.put("signLabel",map.get("sign"));
                }
                list.add(resultMap);
            });
                resultList.add(list);
            }
        );
        objectMap.put("candidateLists",resultList);
        objectMap.put("dict",map);
        String content = "";
        if("1".equals(param.getTemplatePath())){
            content  = readHtmlTemplate("/templates/sign/desk_top_big.html");
        }else {
            content  = readHtmlTemplate("/templates/sign/desk_top_small.html");
        }

      File file =  this.renderTemplateHtml(content,objectMap);
      return file;
    }

    public File generateSignHTML(TaskRoomVo taskRoom, List<CandidateVo> candidateVos,Task task) throws IOException {
        Map<String, Object> objectMap = new HashMap<>();
        objectMap = generateMap(objectMap,taskRoom);
        objectMap.put("taskName",task.getName());

        //10个一组为一页
        int partition = 50;
        List<List<CandidateVo>> parts = Lists.partition(candidateVos,partition);
        List<List<Map<String, String>>> resultList = new ArrayList<>();
        parts.stream().forEach(candidateList ->{
                    List<Map<String, String>> list = new ArrayList<>();
                    candidateList.stream().forEach(c -> {
                        Map<String, String> resultMap = new HashMap<>();
                        resultMap.put("name",c.getCandidateName());
                        resultMap.put("admission",c.getAdmissionNumber());
                        resultMap.put("subject",c.getSubjectName());
                        resultMap.put("seat",c.getSeat());
                        list.add(resultMap);
                    });
                    resultList.add(list);
                }
        );
        objectMap.put("candidateLists",resultList);
        String content  = readHtmlTemplate("/templates/sign/sign.html");
        File file =  this.renderTemplateHtml(content,objectMap);
        return file;
    }

    /**
     * 渲染模板HTML文件
     *
     * @param templateHtml
     * @param templateVariablesMappingData
     * @return
     */
    private File renderTemplateHtml(String templateHtml, Map<String, Object> templateVariablesMappingData) {
        // 字符串模板
        templateHtml = URLUtil.decode(templateHtml, CharsetUtil.UTF_8);
        TemplateEngine engine = new ThymeleafEngine(new TemplateConfig());
        Template template = engine.getTemplate(templateHtml);
        File tempFile = FileUtil.createTempFile(HTML_SUFFIX, Boolean.TRUE);
        tempFile.deleteOnExit();
        if (log.isTraceEnabled()) {
            log.trace("# 进行渲染模板HTML为：{}", templateHtml);
        }
        // 渲染模板HTML文件
        try{
        template.render(templateVariablesMappingData, tempFile);}
        catch (Exception ex){
            ex.printStackTrace();
        }
        if (log.isTraceEnabled()) {
            String path = "sign/html/"+tempFile.getName();
            String fileUrl = "";
            try{
                fileUrl = cloudObsService.updateFile(path, tempFile);
            }catch (Exception ex){
                log.error("# 上传华为云失败：{}", ex.getMessage());
            }
            log.trace("# 模板渲染完成以后的HTML文件URL：{}", fileUrl);
        }
        return tempFile;
    }



    private String readHtmlTemplate(String path) throws IOException {
        ClassPathResource classPathResource = new ClassPathResource(path);
        InputStream inputStream = classPathResource.getInputStream();
        String content = "";
        BufferedReader reader = null;
        StringBuffer buffer = new StringBuffer();
        if(inputStream != null){
            try{
                reader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
                while ((content = reader.readLine()) != null){
                    buffer.append(content);
                }
            } catch (Exception ex){
                if(reader != null){
                    reader.close();
                }
                inputStream.close();

            }
        }
        return buffer.toString();
    }
}
